﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.Reflection;

#if WPF
using System.Windows;
using Silverlight.Hosting.Wpf.Interop;
using Silverlight.Hosting.Wpf.Private;
using Color = System.Windows.Media.Color;

namespace Silverlight.Hosting.Wpf
#else
using Silverlight.Hosting.Interop;
using Silverlight.Hosting.Private;

namespace Silverlight.Hosting
#endif
{
    /// <summary>
    /// The Settings sub-object of the a SilverlightHost.
    /// </summary>
    public sealed class Settings
    {
        internal SafeComObject<object> _comObject;
        private Dictionary<string, object> _config;
        private IHostContainerServicesProvider _host;
#if WPF
        private readonly static Type _ownerType = typeof(Settings);
#endif

        internal Settings(SafeComObject<object> comObject, Dictionary<string, object> config, IHostContainerServicesProvider host)
        {
            _comObject = comObject;
            _config = config;
            _host = host;
        }

        #region Internal Events

        internal event EventHandler<MaxFrameRateChangedEventArgs> MaxFrameRateChanged;

        #endregion

#if WPF
        #region Dependency Properties

        public static readonly DependencyProperty EnableAutoZoomProperty = DependencyProperty.RegisterAttached(
            "EnableAutoZoom", typeof(bool), _ownerType, new PropertyMetadata(true, OnEnableAutoZoomChanged));

        public static readonly DependencyProperty EnableCacheVisualizationProperty = DependencyProperty.RegisterAttached(
            "EnableCacheVisualization", typeof(bool), _ownerType, new PropertyMetadata(false, OnEnableCacheVisualizationChanged));

        public static readonly DependencyProperty EnableFrameRateCounterProperty = DependencyProperty.RegisterAttached(
            "EnableFrameRateCounter", typeof(bool), _ownerType, new PropertyMetadata(false, OnEnableFrameRateCounterChanged));

        public static readonly DependencyProperty EnableGPUAccelerationProperty = DependencyProperty.RegisterAttached(
            "EnableGPUAcceleration", typeof(bool), _ownerType, new PropertyMetadata(true, OnEnableGPUAccelerationChanged));

        public static readonly DependencyProperty EnableHTMLAccessProperty = DependencyProperty.RegisterAttached(
            "EnableHTMLAccess", typeof(bool), _ownerType, new PropertyMetadata(true, OnEnableHTMLAccessChanged));

        public static readonly DependencyProperty EnableRedrawRegionsProperty = DependencyProperty.RegisterAttached(
            "EnableRedrawRegions", typeof(bool), _ownerType, new PropertyMetadata(false, OnEnableRedrawRegionsChanged));

        public static readonly DependencyProperty MaxFrameRateProperty = DependencyProperty.RegisterAttached(
            "MaxFrameRate", typeof(int), _ownerType, new PropertyMetadata(60, OnMaxFrameRateChanged));

        public static readonly DependencyPropertyKey WindowlessPropertyKey = DependencyProperty.RegisterAttachedReadOnly(
            "Windowless", typeof(bool), _ownerType, new PropertyMetadata(false));
        public static readonly DependencyProperty WindowlessProperty = WindowlessPropertyKey.DependencyProperty;

        public static readonly DependencyProperty BackgroundProperty = DependencyProperty.RegisterAttached(
            "Background", typeof(Color), _ownerType, new PropertyMetadata(System.Windows.Media.Colors.White, OnBackgroundChanged));

        public static readonly DependencyProperty CultureProperty = DependencyProperty.RegisterAttached(
            "Culture", typeof(string), _ownerType, new PropertyMetadata(null, OnCultureChanged));

        public static readonly DependencyProperty UICultureProperty = DependencyProperty.RegisterAttached(
            "UICulture", typeof(string), _ownerType, new PropertyMetadata(null, OnUICultureChanged));

        public static readonly DependencyProperty AllowHtmlPopupWindowProperty = DependencyProperty.RegisterAttached(
            "AllowHtmlPopupWindow", typeof(bool), _ownerType, new PropertyMetadata(true, OnAllowHtmlPopupWindowChanged));

        public static readonly DependencyPropertyKey AutoUpgradePropertyKey = DependencyProperty.RegisterAttachedReadOnly(
            "AutoUpgrade", typeof(bool), _ownerType, new PropertyMetadata(false));
        public static readonly DependencyProperty AutoUpgradeProperty = AutoUpgradePropertyKey.DependencyProperty;

        public static readonly DependencyProperty EnableNavigationProperty = DependencyProperty.RegisterAttached(
            "EnableNavigation", typeof(bool), _ownerType, new PropertyMetadata(true, OnEnableNavigationChanged));

        public static readonly DependencyProperty MinRuntimeVersionProperty = DependencyProperty.RegisterAttached(
            "MinRuntimeVersion", typeof(Version), _ownerType, new PropertyMetadata(null, OnMinRuntimeVersionChanged));

        #endregion
#endif

        #region Public Properties

        /// <summary>
        /// Gets or sets a value that indicates whether the Silverlight plug-in will resize its content
        /// based on the current browser zoom setting.
        /// </summary>
        public bool EnableAutoZoom
        {
            get { return (bool)GetProperty("EnableAutoZoom", true); }
            set { SetProperty("EnableAutoZoom", value); }
        }

        /// <summary>
        /// Gets or sets a value that indicates whether to use a non-production analysis visualization mode, which
        /// shows areas of a page that are being GPU accelerated with a colored overlay. Do not use in production code.
        /// </summary>
        public bool EnableCacheVisualization
        {
            get { return (bool)GetProperty("EnableCacheVisualization", false); }
            set { SetProperty("EnableCacheVisualization", value); }
        }

        /// <summary>
        /// Gets or sets a value that indicates whether to display the current frame rate in the hosting browser's status bar.
        /// </summary>
        public bool EnableFrameRateCounter
        {
            get { return (bool)GetProperty("EnableFrameRateCounter", false); }
            set { SetProperty("EnableFrameRateCounter", value); }
        }

        /// <summary>
        /// Gets a value that indicates whether to use graphics processor unit (GPU) hardware acceleration for
        /// cached compositions, which potentially results in graphics optimization.
        /// </summary>
        public bool EnableGPUAcceleration
        {
            get { return (bool)GetProperty("EnableGPUAcceleration", false); }
            set
            {
                if (!_host.IsCreated)
                {
                    SetProperty("EnableGPUAcceleration", value);
                }
                else
                {
                    throw new InvalidOperationException("This property should be set before the control is created.");
                }
            }
        }

        /// <summary>
        /// Gets a value that indicates whether the Silverlight plug-in allows hosted content or
        /// its runtime to access the HTML DOM.
        /// </summary>
        public bool EnableHTMLAccess
        {
            get { return (bool)GetProperty("EnableHTMLAccess", true); }
            set { SetProperty("EnableHTMLAccess", value); }
        }

        /// <summary>
        /// Gets or sets a value that indicates whether to show the areas of the Silverlight
        /// plug-in that are being redrawn each frame.
        /// </summary>
        public bool EnableRedrawRegions
        {
            get { return (bool)GetProperty("EnableRedrawRegions", false); }
            set { SetProperty("EnableRedrawRegions", value); }
        }

        /// <summary>
        /// Gets or sets the maximum number of frames to render per second.
        /// </summary>
        public int MaxFrameRate
        {
            get { return (int)(uint)GetProperty("MaxFrameRate", (uint)60); }
            set
            {
                int oldValue = 0;
                try
                {
                    oldValue = (int)(uint)GetProperty("MaxFrameRate", (uint)60);
                }
                catch
                {
                    if (_config.ContainsKey("MaxFrameRate"))
                    {
                        oldValue = (int)(uint)_config["MaxFrameRate"];
                    }
                }

                SetProperty("MaxFrameRate", (uint)value);
                
                if (oldValue != value)
                {
                    OnMaxFrameRateChanged(new MaxFrameRateChangedEventArgs()
                    {
                        OldValue = oldValue,
                        NewValue = value
                    });
                }
            }
        }

        /// <summary>
        /// Gets a value that indicates whether the Silverlight plug-in displays as a windowless plug-in.
        /// </summary>
        public bool Windowless
        {
            get { return (bool)GetProperty("Windowless", _host.Host.IsWindowless); }
            internal set { throw new NotImplementedException(); }
        }

        /// <summary>
        /// Gets or sets the background color of the Silverlight plug-in.
        /// </summary>
        public Color Background
        {
            get
            {
                string value = (string)GetProperty("Background", "#FFFFFFFF");

#if WPF
                if (value != null) return (Color)System.Windows.Media.ColorConverter.ConvertFromString(value);
                return System.Windows.Media.Colors.White;
#else
                if (value != null) return (Color)new ColorConverter().ConvertFromString(value);
                return Color.White;
#endif
            }
            set
            {
                SetProperty("Background", string.Format("#{0}{1}{2}{3}", value.A.ToString("X2"), value.R.ToString("X2"),
                    value.G.ToString("X2"), value.B.ToString("X2")));
            }
        }

        /// <summary>
        /// Gets or sets current culture.
        /// </summary>
        public string Culture
        {
            get { return (string)GetProperty("Culture", null); }
            set { SetProperty("Culture", value); }
        }

        /// <summary>
        /// Gets or sets the current user interface (UI) culture.
        /// </summary>
        public string UICulture
        {
            get { return (string)GetProperty("UICulture", null); }
            set { SetProperty("UICulture", value); }
        }

        // Notes
        // --------------------------------------------------------------------------------------------------------------
        // Setting AutoUpgrade causes an access violation errors when the property bag is being loaded,
        // so it will only be read-only. Attempting to read the value of AutoUpgrade, EnableNavigation
        // AllowHtmlPopupWindow or MinRuntimeVersion always throws E_FAIL for some reason, so the values
        // will be read from the _config cache.

        /// <summary>
        /// Gets or sets a value that indicates whether the hosted content in the Silverlight plug-in can use
        /// the HtmlPage.PopupWindow method to display a new browser window.
        /// </summary>
        public bool AllowHtmlPopupWindow
        {
            get { return _config.ContainsKey("AllowHtmlPopupWindow") ? (bool)_config["AllowHtmlPopupWindow"] : true; }
            set
            {
                if (!_host.IsCreated)
                {
                    SetProperty("AllowHtmlPopupWindow", value);
                }
                else
                {
                    throw new InvalidOperationException("This property should be set before the control is created.");
                }
            }
        }

        /// <summary>
        /// Gets a value that indicates whether a Silverlight plug-in version earlier than the specified
        /// MinRuntimeVersion will attempt to update automatically. Setting autoUpgrade has no effect unless
        /// you also set MinRuntimeVersion.
        /// </summary>
        public bool AutoUpgrade
        {
            get { return _config.ContainsKey("AutoUpgrade") ? (bool)_config["AutoUpgrade"] : false; }
            internal set
            {
                throw new NotImplementedException();
                //if (!_host.IsCreated)
                //{
                //    SetProperty("AutoUpgrade", value);
                //}
                //else
                //{
                //    throw new InvalidOperationException("This property should be set before the control is created.");
                //}
            }
        }

        /// <summary>
        /// Gets or sets a value that indicates whether the hosted content in the Silverlight plug-in can use a
        /// HyperlinkButton to navigate to external URIs.
        /// </summary>
        public bool EnableNavigation
        {
            get { return (_config.ContainsKey("EnableNavigation") ? (string)_config["EnableNavigation"] : "all") == "all"; }
            set
            {
                if (!_host.IsCreated)
                {
                    SetProperty("EnableNavigation", value ? "all" : "none");
                }
                else
                {
                    throw new InvalidOperationException("This property should be set before the control is created.");
                }
            }
        }

        /// <summary>
        /// Gets or sets a value that indicates the earliest Silverlight version that is required in order
        /// to run a Silverlight-based application.
        /// </summary>
        [System.ComponentModel.TypeConverter(typeof(VersionConverter))]
        public Version MinRuntimeVersion
        {
            get
            {
                string version = _config.ContainsKey("MinRuntimeVersion") ? (string)_config["MinRuntimeVersion"] : null;
                return version != null ? new Version(version) : null;
            }
            set
            {
                if (!_host.IsCreated)
                {
                    SetProperty("MinRuntimeVersion", value != null ? value.ToString() : null);
                }
                else
                {
                    throw new InvalidOperationException("This property should be set before the control is created.");
                }
            }
        }

        #endregion

        #region Public Methods

#if WPF

        public static void SetEnableAutoZoom(DependencyObject d, bool value)
        {
            d.SetValue(EnableAutoZoomProperty, value);
        }

        public static bool GetEnableAutoZoom(DependencyObject d)
        {
            return (bool)d.GetValue(EnableAutoZoomProperty);
        }

        public static void SetEnableCacheVisualization(DependencyObject d, bool value)
        {
            d.SetValue(EnableCacheVisualizationProperty, value);
        }

        public static bool GetEnableCacheVisualization(DependencyObject d)
        {
            return (bool)d.GetValue(EnableCacheVisualizationProperty);
        }

        public static void SetEnableFrameRateCounter(DependencyObject d, bool value)
        {
            d.SetValue(EnableFrameRateCounterProperty, value);
        }

        public static bool GetEnableFrameRateCounter(DependencyObject d)
        {
            return (bool)d.GetValue(EnableFrameRateCounterProperty);
        }

        public static void SetEnableGPUAcceleration(DependencyObject d, bool value)
        {
            d.SetValue(EnableGPUAccelerationProperty, value);
        }

        public static bool GetEnableGPUAcceleration(DependencyObject d)
        {
            return (bool)d.GetValue(EnableGPUAccelerationProperty);
        }

        public static void SetEnableHTMLAccess(DependencyObject d, bool value)
        {
            d.SetValue(EnableHTMLAccessProperty, value);
        }

        public static bool GetEnableHTMLAccess(DependencyObject d)
        {
            return (bool)d.GetValue(EnableHTMLAccessProperty);
        }

        public static void SetEnableRedrawRegions(DependencyObject d, bool value)
        {
            d.SetValue(EnableRedrawRegionsProperty, value);
        }

        public static bool GetEnableRedrawRegions(DependencyObject d)
        {
            return (bool)d.GetValue(EnableRedrawRegionsProperty);
        }

        public static void SetMaxFrameRate(DependencyObject d, int value)
        {
            d.SetValue(MaxFrameRateProperty, value);
        }

        public static int GetMaxFrameRate(DependencyObject d)
        {
            return (int)d.GetValue(MaxFrameRateProperty);
        }

        internal static void SetWindowless(DependencyObject d, bool value)
        {
            if (d is ISilverlightHost)
            {
                d.SetValue(WindowlessPropertyKey, value);
            }
        }

        public static bool GetWindowless(DependencyObject d)
        {
            return (bool)d.GetValue(WindowlessProperty);
        }

        public static void SetBackground(DependencyObject d, Color value)
        {
            d.SetValue(BackgroundProperty, value);
        }

        public static Color GetBackground(DependencyObject d)
        {
            return (Color)d.GetValue(BackgroundProperty);
        }

        public static void SetCulture(DependencyObject d, string value)
        {
            d.SetValue(CultureProperty, value);
        }

        public static string GetCulture(DependencyObject d)
        {
            return (string)d.GetValue(CultureProperty);
        }

        public static void SetUICulture(DependencyObject d, string value)
        {
            d.SetValue(UICultureProperty, value);
        }

        public static string GetUICulture(DependencyObject d)
        {
            return (string)d.GetValue(UICultureProperty);
        }

        public static void SetAllowHtmlPopupWindow(DependencyObject d, bool value)
        {
            d.SetValue(AllowHtmlPopupWindowProperty, value);
        }

        public static bool GetAllowHtmlPopupWindow(DependencyObject d)
        {
            return (bool)d.GetValue(AllowHtmlPopupWindowProperty);
        }

        internal static void SetAutoUpgrade(DependencyObject d, bool value)
        {
            d.SetValue(AutoUpgradePropertyKey, value);
        }

        public static bool GetAutoUpgrade(DependencyObject d)
        {
            return (bool)d.GetValue(AutoUpgradeProperty);
        }

        public static void SetEnableNavigation(DependencyObject d, bool value)
        {
            d.SetValue(EnableNavigationProperty, value);
        }

        public static bool GetEnableNavigation(DependencyObject d)
        {
            return (bool)d.GetValue(EnableNavigationProperty);
        }

        public static void SetMinRuntimeVersion(DependencyObject d, Version value)
        {
            d.SetValue(MinRuntimeVersionProperty, value);
        }

        public static Version GetMinRuntimeVersion(DependencyObject d)
        {
            return (Version)d.GetValue(MinRuntimeVersionProperty);
        }

#endif

        #endregion

        #region Private Methods

        private void OnMaxFrameRateChanged(MaxFrameRateChangedEventArgs e)
        {
            if (MaxFrameRateChanged != null)
            {
                MaxFrameRateChanged(this, e);
            }
        }

        private object GetProperty(string name, object @default)
        {
            if (!_host.IsCreated)
            {
                if (_config.ContainsKey(name)) return _config[name];
                else return @default;
                //throw new AxHost.InvalidActiveXStateException(name, AxHost.ActiveXInvokeKind.PropertyGet);
            }
            else
            {
                return _comObject.Object.GetType().InvokeMember(name, BindingFlags.GetProperty, null, _comObject.Object, null);
            }
        }

        private void SetProperty(string name, object value)
        {
            if (_host.IsCreated)
            {
                _comObject.Object.GetType().InvokeMember(name, BindingFlags.SetProperty, null, _comObject.Object, new object[] { value });
            }
            _config[name] = value;
        }

#if WPF

        private static void OnEnableAutoZoomChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is ISilverlightHost)
            {
                (d as ISilverlightHost).Settings.EnableAutoZoom = (bool)e.NewValue;
            }
        }

        private static void OnEnableCacheVisualizationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is ISilverlightHost)
            {
                (d as ISilverlightHost).Settings.EnableCacheVisualization = (bool)e.NewValue;
            }
        }

        private static void OnEnableFrameRateCounterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is ISilverlightHost)
            {
                (d as ISilverlightHost).Settings.EnableFrameRateCounter = (bool)e.NewValue;
            }
        }

        private static void OnEnableGPUAccelerationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is ISilverlightHost)
            {
                (d as ISilverlightHost).Settings.EnableGPUAcceleration = (bool)e.NewValue;
            }
        }

        private static void OnEnableHTMLAccessChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is ISilverlightHost)
            {
                (d as ISilverlightHost).Settings.EnableHTMLAccess = (bool)e.NewValue;
            }
        }

        private static void OnEnableRedrawRegionsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is ISilverlightHost)
            {
                (d as ISilverlightHost).Settings.EnableRedrawRegions = (bool)e.NewValue;
            }
        }

        private static void OnMaxFrameRateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is ISilverlightHost)
            {
                (d as ISilverlightHost).Settings.MaxFrameRate = (int)e.NewValue;
            }
        }

        private static void OnBackgroundChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is ISilverlightHost)
            {
                (d as ISilverlightHost).Settings.Background = (Color)e.NewValue;
            }
        }

        private static void OnCultureChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is ISilverlightHost)
            {
                (d as ISilverlightHost).Settings.Culture = (string)e.NewValue;
            }
        }

        private static void OnUICultureChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is ISilverlightHost)
            {
                (d as ISilverlightHost).Settings.UICulture = (string)e.NewValue;
            }
        }

        private static void OnAllowHtmlPopupWindowChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is ISilverlightHost)
            {
                (d as ISilverlightHost).Settings.AllowHtmlPopupWindow = (bool)e.NewValue;
            }
        }

        private static void OnEnableNavigationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is ISilverlightHost)
            {
                (d as ISilverlightHost).Settings.EnableNavigation = (bool)e.NewValue;
            }
        }

        private static void OnMinRuntimeVersionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is ISilverlightHost)
            {
                (d as ISilverlightHost).Settings.MinRuntimeVersion = (Version)e.NewValue;
            }
        }

#endif

        #endregion
    }
}
